package rest.jbpm.filters; 

import rest.jbpm.wrappers.ResourceWrapper;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import javax.xml.bind.JAXBException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;
import com.sun.jersey.api.core.ResourceConfig;
import com.sun.jersey.api.uri.UriTemplate;
import com.sun.jersey.spi.container.ContainerRequest;
import com.sun.jersey.spi.container.ContainerResponse;
import com.sun.jersey.spi.container.ContainerResponseFilter;

public class UnwrappingFilter implements ContainerResponseFilter {

	
	private static Logger log = LoggerFactory.getLogger(ContainerResponseFilter.class);
	private SAXParserFactory factory = SAXParserFactory.newInstance();
	@Context private ResourceConfig rc;
    @Context private UriInfo ui;
    private ArrayList<ResourceWrapper> knownResources = new ArrayList<ResourceWrapper>(); 
    public static final String UNWRAP = "unwrap";

	
	@Override
	public ContainerResponse filter(ContainerRequest request,
			ContainerResponse response) {
		
		if(!inited) init();
		if(!request.getQueryParameters().containsKey(UNWRAP)) {
			return response;
		}
		try{
			
			SAXParser parser = factory.newSAXParser();
			try {
				
				if(response.getEntity() instanceof String) {
					log.debug("parsing response");
					ByteArrayOutputStream baos = new ByteArrayOutputStream();

					parser.parse(
						new ByteArrayInputStream(((String)response.getEntity()).getBytes("UTF-8")), 
						new TagHandler(baos, knownResources));
					
					response.setEntity(baos.toString("UTF-8"));
					
				} else {
					log.debug("responce is not a string");
				}
				
			} catch (JAXBException e) {
				log.error("failed to filter responce");
				e.printStackTrace();
				throw new WebApplicationException(e, 500);
			}
			
		} catch (IOException e) {
			e.printStackTrace();
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		}

		return response;
	}
	
	private boolean inited = false;
    
	private void init() {
		
		log.debug("initing filter");
		for(Class<?> cls : rc.getRootResourceClasses()) {
			
			String classBase = null;

			if(cls.isAnnotationPresent(Path.class)){
                classBase = ((Path)cls.getAnnotation(Path.class)).value();
            }

			for(Method mtd : cls.getMethods()){
				
				if(mtd.isAnnotationPresent(GET.class)) {
					try {
						
						Object instance=cls.newInstance();
						for(Field f: cls.getDeclaredFields()){
							if(f.isAnnotationPresent(Context.class)){
								if(f.getType().equals(UriInfo.class)){
									f.setAccessible(true);
									f.set(instance, ui);
								}
							}
							
						}
						UriBuilder builder = UriBuilder.fromUri(ui.getBaseUri());
						
						knownResources.add(new ResourceWrapper(new UriTemplate( 
								builder.scheme("http").build(null).toString() 
								+ classBase + "/" +
								(mtd.isAnnotationPresent(Path.class) ?
										((Path)mtd.getAnnotation(Path.class)).value() : "")), instance, mtd));
						
						knownResources.add(new ResourceWrapper(new UriTemplate( 
								builder.scheme("https").build(null).toString() 
								+ classBase + "/" +
								(mtd.isAnnotationPresent(Path.class) ?
										((Path)mtd.getAnnotation(Path.class)).value() : "")), instance, mtd));
						
					} catch (InstantiationException e) {
						e.printStackTrace();
					} catch (IllegalAccessException e) {
						e.printStackTrace();
					}
					
				}
            }
		}
		
		for(ResourceWrapper rw : knownResources)
			log.debug("{} mapped to {}.{}",
                    new Object [] {rw.getUriTemplate().toString(),
                        rw.getResource().getClass().getSimpleName(),
                        rw.getMethod().getName()});
		
		inited = true;

	}
	
}
